﻿using Credera.Theta.API;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Credera.Theta.Component
{
    class CommandValidator
    {
        public static void Validate(InputCommandDef def)
        {
            ValidateAliases(def);
            ValidateParameterSets(def);
            ValidateValueValidationFunctions(def);
        }

        private static void ValidateValueValidationFunctions(InputCommandDef def)
        {
            foreach (var param in def.Parameters)
            {
                if (param.ValidationValuesFunction != null
                    && def.InputCommandType.GetMethod(param.ValidationValuesFunction) == null)
                    throw new InvalidCommandException(def.InputCommandType
                        , string.Format("The parameter '{0}' for command {1} defines a validation values function '{2}' which cannot be found"
                        , param.Name, def.InputCommandType, param.ValidationValuesFunction));
            }
        }

        private static void ValidateAliases(InputCommandDef def)
        {
            // validate parameters

            List<string> uniqueParameterNames = new List<string>();
            foreach (var param in def.Parameters)
            {
                if (uniqueParameterNames.Contains(param.Name, StringComparer.OrdinalIgnoreCase))
                    throw new InvalidCommandException(def.InputCommandType
                        , string.Format("The command {0} contains a duplicate parameter name or alias '{1}'", param.Name));

                uniqueParameterNames.Add(param.Name);

                foreach (var alias in param.Aliases)
                {
                    if(uniqueParameterNames.Contains(alias, StringComparer.OrdinalIgnoreCase))
                        throw new InvalidCommandException(def.InputCommandType
                            , string.Format("The command {0} contains a duplicate parameter name or alias '{1}'", param.Name));

                    uniqueParameterNames.Add(alias);
                }
            }

            // validate command aliases

            List<string> commandNames = new List<string>();
            commandNames.Add(def.CommandName);

            foreach (var alias in def.Aliases)
            {
                if(commandNames.Contains(alias, StringComparer.OrdinalIgnoreCase))
                    throw new InvalidCommandException(def.InputCommandType
                        , string.Format("The command {0} contains a duplicate command name or alias '{1}'", alias));
            }
        }

        private static void ValidateParameterSets(InputCommandDef info)
        {
            var usedParameterSets = info.Parameters.Where(p => !string.IsNullOrEmpty(p.ParameterSet)).Select(p => p.ParameterSet).Distinct().ToList();
            if (usedParameterSets.Count > 0 && string.IsNullOrEmpty(info.DefaultParameterSet))
                throw new InvalidCommandException(info.InputCommandType
                    , string.Format("The input command {0} has parameters that define parameter sets, but a default parameter set is not defined",
                        info.InputCommandType.FullName));

            if (!string.IsNullOrEmpty(info.DefaultParameterSet) && !usedParameterSets.Contains(info.DefaultParameterSet))
                    throw new InvalidCommandException(info.InputCommandType
                        , string.Format("The input command {0} defines a default parameter set, but no parameters are in the defined set"
                            , info.InputCommandType.FullName));
        }
    }
}
